/**
 * Copyright  2025, Hai Yue Xing He  ZHAO LIMIN
 *
 * @author        ZHAO LIMIN
 * @version       1.0.1
 * @since         2025-04
 * public static ts   A 3D Cartesian point.
 */
import { Check } from './Check'
import { defined } from './Defined'
import { DeveloperError } from './DeveloperError'
import { HYXHMathUtil } from './HYXHMathUtil'
import { defaultValue } from './HYXHDefaultValue'
import { Quaternion } from './Quaternion'
import { IPackable, packs, unpacks, packArray, unpackArray } from './Packable'
import { UNION_NUMBER_ARRAY } from './HDataType'
import { ICartesian } from './Cartesian'
import { IArrayIndex, INumberArray } from './HXArray'

export interface ICartesian3D extends ICartesian{
  x: number
  y: number
  z: number
}

export class Cartesian3 implements ICartesian3D, IPackable, INumberArray, IArrayIndex {
    [index: number]: number;  
    public get x(): number {
      return this[0]
    }
    public set x(v:number) {
      this[0] = v??0.0
    }
    //public y: number
    public get y(): number {
      return this[1]
    }
    public set y(v:number) {
      this[1] = v??0.0
    }

    public get z(): number {
      return this[2]
    }
    public set z(v:number) {
      this[2] = v??0.0
    }    
    public get length(): number {
      return Cartesian3.packedLength 
    }
    
    /**
     * A 3D Cartesian point.
     * @alias Cartesian3
     * @constructor
     *
     * @param {number} [x=0.0] The X component.
     * @param {number} [y=0.0] The Y component.
     * @param {number} [z=0.0] The Z component.
     *
     * @see Cartesian2
     * @see Cartesian4
     * @see Packable
     */
    constructor(x?: number, y?: number, z?: number) {
        /**
         * The X component.
         * @type {number}
         * @default 0.0
         */
        this.x = x ?? 0.0

        /**
         * The Y component.
         * @type {number}
         * @default 0.0
         */
        this.y = y ?? 0.0

        /**
         * The Z component.
         * @type {number}
         * @default 0.0
         */
        this.z = z ?? 0.0
    }
    push(...items: number[]): number[] {
        this[0] = items[0]
        this[1] = items[1]
        this[2] = items[2]
        return [this[0], this[1], this[2]]  
    }

    
    /**
     * Creates a Cartesian3 instance from x, y and z coordinates.
     *
     * @param {number} x The x coordinate.
     * @param {number} y The y coordinate.
     * @param {number} z The z coordinate.
     * @param {Cartesian3} [result] The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided.
     */
    public static fromElements(
        x: number, y: number, z: number, 
        result?: ICartesian): ICartesian | undefined {
        if (!defined(result)) {
            return new Cartesian3(x, y, z)
        }
        result!.x = x
        result!.y = y
        result!.z = z
        return result
    }
    /**
     * Duplicates a Cartesian3 instance.
     *
     * @param {Cartesian3} cartesian The Cartesian to duplicate.
     * @param {Cartesian3} [result] The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. (Returns undefined if cartesian is undefined)
     */
    public static clone(
        cartesian: ICartesian,
        result?: ICartesian): ICartesian | undefined {
        if (!defined(cartesian)) {
            return undefined
        }
        if (!defined(result)) {
            return new Cartesian3(cartesian.x, cartesian.y, cartesian.z)
        }

        result!.x = cartesian.x
        result!.y = cartesian.y
        result!.z = cartesian.z
        return result
    }
    /**
     * Creates a Cartesian3 instance from an existing Cartesian4.  This simply takes the
     * x, y, and z properties of the Cartesian4 and drops w.
     * @function
     *
     * @param {Cartesian4} cartesian The Cartesian4 instance to create a Cartesian3 instance from.
     * @param {Cartesian3} [result] The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided.
     */
    public static readonly fromCartesian4 = Cartesian3.clone
    /**
     * The number of elements used to pack the object into an array.
     * @type {number}
     */
    public static readonly packedLength = 3
    /**
     * @override IPackable
     * @returns
     * @see Packable.IPackable
     */
    public getPackedLength(): number {
        return Cartesian3.packedLength
    }
    public getRowLength(): number {
        return 1
    }
    public getColumnLength(): number {
        return Cartesian3.packedLength
    }
    /**
     * Stores the provided instance into the provided array.
     *
     * @param {Cartesian3} value The value to pack.
     * @param {number[]} array The array to pack into.
     * @param {number} [startingIndex=0] The index into the array at which to start packing the elements.
     *
     * @returns {number[]} The array that was packed into
     */
    public static pack(
        value: ICartesian,
        array: UNION_NUMBER_ARRAY,
        startingIndex: number,
    ): UNION_NUMBER_ARRAY {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('value', value)
        Check.defined('array', array)
        Check.typeOf.tnumber.checkInteger('startingIndex', startingIndex)
        //>>includeEnd('debug');
        startingIndex = startingIndex ?? 0
        packs(value as Cartesian3, array, startingIndex)
        return array
    }
    /**
     * Stores the provided instance into the provided array.
     *
     * @param {UNION_NUMBER_ARRAY} array The array to pack into.
     * @param {number} [startingIndex=0] The index into the array at which to start packing the elements.
     *
     */
    public pack(array: UNION_NUMBER_ARRAY, startingIndex: number = 0) {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('array', array)
        Check.defined('array', array)
        Check.typeOf.tnumber.checkInteger('startingIndex', startingIndex)
        //>>includeEnd('debug');
        startingIndex = startingIndex ?? 0
        array[startingIndex++] = this.x
        array[startingIndex++] = this.y
        array[startingIndex] = this.z
        return array
    }
    /**
     * Retrieves an instance from a packed array.
     *
     * @param {number[]} array The packed array.
     * @param {number} [startingIndex=0] The starting index of the element to be unpacked.
     * @param {Cartesian3} [result] The object into which to store the result.
     * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided.
     */
    public static unpack(
        array: UNION_NUMBER_ARRAY,
        startingIndex: number,
        result?: ICartesian,
    ): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.defined('array', array)
        //>>includeEnd('debug');
        startingIndex = startingIndex ?? 0
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result = unpacks(array, startingIndex, result as Cartesian3) as Cartesian3
        return result
    }
    /**
     * Retrieves an instance from a packed array.
     *
     * @param {number[]} array The packed array.
     * @param {number} [startingIndex=0] The starting index of the element to be unpacked.
     */
    public unpack(array: UNION_NUMBER_ARRAY, startingIndex: number = 0) {
        //>>includeStart('debug', pragmas.debug);
        Check.defined('array', array)
        //>>includeEnd('debug');
        startingIndex = startingIndex ?? 0
        this.x = array[startingIndex++]
        this.y = array[startingIndex++]
        this.z = array[startingIndex]
    }
    /**
     * Flattens an array of Cartesian3s into an array of components.
     *
     * @param {Cartesian3[]} array The array of cartesians to pack.
     * @param {number[]} [result] The array onto which to store the result. If this is a typed array,
     * it must have array.length * 3 components, else a {@link DeveloperError} will be thrown. If it is a regular
     * array, it will be resized to have (array.length * 3) elements.
     * @returns {number[]} The packed array.
     */
    public static packArray(
        array: Array<ICartesian>,
        result: UNION_NUMBER_ARRAY):UNION_NUMBER_ARRAY {
        //>>includeStart('debug', pragmas.debug);
        Check.defined('array', array)
        //>>includeEnd('debug');

        const length = array.length
        const resultLength = length * Cartesian3.packedLength
        if (!defined(result)) {
            result = new Array(resultLength)
        } else if (!Array.isArray(result)) {
            //>>includeStart('debug', pragmas.debug);
            throw new DeveloperError(
                'If result is a typed array, it must have exactly array.length * 3 elements',
            )
            //>>includeEnd('debug');
        } else if (result.length !== resultLength) {
            result.length = resultLength
        }
        packArray(array as Cartesian3[], result)
        return result
    }
    /**
     * Unpacks an array of cartesian components into an array of Cartesian3s.
     *
     * @param {number[]} array The array of components to unpack.
     * @param {Cartesian3[]} [result] The array onto which to store the result.
     * @returns {Cartesian3[]} The unpacked array.
     */
    public static unpackArray(
        array: UNION_NUMBER_ARRAY,
        result: Array<ICartesian>): Array<ICartesian> {
        //>>includeStart('debug', pragmas.debug);
        Check.defined('array', array)
        Check.typeOf.tnumber.greaterThanOrEquals('array.length', array.length, Cartesian3.packedLength)
        if (array.length % Cartesian3.packedLength !== 0) {
            throw new DeveloperError('array length must be a multiple of 3.')
        }
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Array(length / Cartesian3.packedLength)
            for (let i = 0; i < result.length; ++i) {
                result[i] = new Cartesian3()
            }
        } else {
            result.length = length / Cartesian3.packedLength
        }
        unpackArray(array, result as Cartesian3[])
        return result
    }
    /**
     * Creates a Cartesian3 from three consecutive elements in an array.
     * @function
     *
     * @param {number[]} array The array whose three consecutive elements correspond to the x, y, and z components, *
     * respectively.
     * @param {number} [startingIndex=0] The offset into the array of the first element, which corresponds to the x *
     * component.
     * @param {Cartesian3} [result] The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided.
     *
     * @example
     * // Create a Cartesian3 with (1.0, 2.0, 3.0)
     * const v = [1.0, 2.0, 3.0];
     * const p = Cartesian3.fromArray(v);
     *
     * // Create a Cartesian3 with (1.0, 2.0, 3.0) using an offset into an array
     * const v2 = [0.0, 0.0, 1.0, 2.0, 3.0];
     * const p2 = Cartesian3.fromArray(v2, 2);
     */
    public static readonly fromArray = Cartesian3.unpack // Cartesian3.unpack

    /**
     * Computes the value of the maximum component for the supplied Cartesian.
     *
     * @param {Cartesian3} cartesian The cartesian to use.
     * @returns {number} The value of the maximum component.
     */
    public static maximumComponent(cartesian: ICartesian): number {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('cartesian', cartesian)
        //>>includeEnd('debug');

        return Math.max(cartesian.x, cartesian.y, cartesian.z!)
    }
    /**
     * Computes the value of the minimum component for the supplied Cartesian.
     *
     * @param {Cartesian3} cartesian The cartesian to use.
     * @returns {number} The value of the minimum component.
     */
    public static minimumComponent(cartesian: ICartesian): number {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('cartesian', cartesian)
        //>>includeEnd('debug');

        return Math.min(cartesian.x, cartesian.y, cartesian.z!)
    }
    /**
     * Compares two Cartesians and computes a Cartesian which contains the minimum components of the supplied Cartesians.
     *
     * @param {Cartesian3} first A cartesian to compare.
     * @param {Cartesian3} second A cartesian to compare.
     * @param {Cartesian3} result The object into which to store the result.
     * @returns {Cartesian3} A cartesian with the minimum components.
     */
    public static minimumByComponent(
        first: ICartesian,
        second: ICartesian,
        result?: ICartesian,
    ): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('first', first)
        Check.typeOf.object('second', second)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result!.x = Math.min(first.x, second.x)
        result!.y = Math.min(first.y, second.y)
        result!.z = Math.min(first.z!, second.z!)
        return result
    }
    /**
     * Compares two Cartesians and computes a Cartesian which contains the maximum components of the supplied Cartesians.
     *
     * @param {Cartesian3} first A cartesian to compare.
     * @param {Cartesian3} second A cartesian to compare.
     * @param {Cartesian3} result The object into which to store the result.
     * @returns {Cartesian3} A cartesian with the maximum components.
     */
    public static maximumByComponent(
        first: ICartesian,
        second: ICartesian,
        result?: ICartesian,
    ): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('first', first)
        Check.typeOf.object('second', second)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result!.x = Math.max(first.x, second.x)
        result!.y = Math.max(first.y, second.y)
        result!.z = Math.max(first.z!, second.z!)
        return result
    }
    /**
     * Constrain a value to lie between two values.
     *
     * @param {Cartesian3} cartesian The value to clamp.
     * @param {Cartesian3} min The minimum bound.
     * @param {Cartesian3} max The maximum bound.
     * @param {Cartesian3} result The object into which to store the result.
     * @returns {Cartesian3} The clamped value such that min <= value <= max.
     */
    public static clamp(
        value: ICartesian,
        min: ICartesian,
        max: ICartesian,
        result?: ICartesian,
    ): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('value', value)
        Check.typeOf.object('min', min)
        Check.typeOf.object('max', max)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        const x = HYXHMathUtil.clamp(value.x, min.x, max.x)
        const y = HYXHMathUtil.clamp(value.y, min.y, max.y)
        const z = HYXHMathUtil.clamp(value.z!, min.z!, max.z!)
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result!.x = x
        result!.y = y
        result!.z = z
        return result
    }
    /**
     * Computes the provided Cartesian's squared magnitude.
     *
     * @param {Cartesian3} cartesian The Cartesian instance whose squared magnitude is to be computed.
     * @returns {number} The squared magnitude.
     */
    public static magnitudeSquared(cartesian: ICartesian): number {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('cartesian', cartesian)
        //>>includeEnd('debug');
        return cartesian.x * cartesian.x + cartesian.y * cartesian.y + cartesian.z! * cartesian.z!
    }
    /**
     * Computes the Cartesian's magnitude (length).
     *
     * @param {Cartesian3} cartesian The Cartesian instance whose magnitude is to be computed.
     * @returns {number} The magnitude.
     */
    public static magnitude(cartesian: ICartesian): number {
        return Math.sqrt(Cartesian3.magnitudeSquared(cartesian))
    }
    public static readonly distanceScratch = new Cartesian3()
    /**
     * Computes the distance between two points.
     *
     * @param {Cartesian3} left The first point to compute the distance from.
     * @param {Cartesian3} right The second point to compute the distance to.
     * @returns {number} The distance between two points.
     *
     * @example
     * // Returns 1.0
     * const d = Cartesian3.distance(new Cartesian3(1.0, 0.0, 0.0), new Cartesian3(2.0, 0.0, 0.0));
     */
    public static distance(
        left: ICartesian,
        right: ICartesian,
        distanceScratch?: ICartesian,
    ): number {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('left', left)
        Check.typeOf.object('right', right)
        //>>includeEnd('debug');
        if (!defined(distanceScratch)) {
            distanceScratch = new Cartesian3()
        }
        Cartesian3.subtract(left, right, Cartesian3.distanceScratch)
        if (defined(distanceScratch)) {
            Cartesian3.clone(Cartesian3.distanceScratch, distanceScratch)
        }
        return Cartesian3.magnitude(Cartesian3.distanceScratch)
    }
    /**
     * Computes the squared distance between two points.  Comparing squared distances
     * using this function is more efficient than comparing distances using {@link Cartesian3#distance}.
     *
     * @param {Cartesian3} left The first point to compute the distance from.
     * @param {Cartesian3} right The second point to compute the distance to.
     * @returns {number} The distance between two points.
     *
     * @example
     * // Returns 4.0, not 2.0
     * const d = public static distanceSquared(new Cartesian3(1.0, 0.0, 0.0),
     * new Cartesian3(3.0, 0.0, 0.0));
     */
    public static distanceSquared(left: ICartesian, right: ICartesian): number {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('left', left)
        Check.typeOf.object('right', right)
        //>>includeEnd('debug');
        
        Cartesian3.subtract(left, right, Cartesian3.distanceScratch)
        return Cartesian3.magnitudeSquared(Cartesian3.distanceScratch)
    }
    /**
     * Computes the normalized form of the supplied Cartesian.
     *
     * @param {Cartesian3} cartesian The Cartesian to be normalized.
     * @param {Cartesian3} result The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter.
     */
    public static normalize(
        cartesian: ICartesian, result?: ICartesian): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('cartesian', cartesian)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        const magnitude = Cartesian3.magnitude(cartesian)
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result!.x = cartesian.x / magnitude
        result!.y = cartesian.y / magnitude
        result!.z = cartesian.z! / magnitude
        //>>includeStart('debug', pragmas.debug);
        if (isNaN(result!.x) || isNaN(result!.y) || isNaN(result!.z)) {
            throw new DeveloperError('normalized result is not a number')
        }
        //>>includeEnd('debug');
        return result
    }
    /**
     * 计算两个笛卡尔坐标系的点积（标量积）。
     *
     * @param {Cartesian3} left The first Cartesian.
     * @param {Cartesian3} right The second Cartesian.
     * @returns {number} The dot product.
     */
    public static dot(left: ICartesian, right: ICartesian) {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('left', left)
        Check.typeOf.object('right', right)
        //>>includeEnd('debug');
        return left.x * right.x + left.y * right.y + left.z! * right.z!
    }
    /**
     * 计算两个笛卡尔坐标系的分量乘积。
     *
     * @param {Cartesian3} left The first Cartesian.
     * @param {Cartesian3} right The second Cartesian.
     * @param {Cartesian3} result The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter.
     */
    public static multiplyComponents(
        left: ICartesian,
        right: ICartesian,
        result?: ICartesian,
    ): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('left', left)
        Check.typeOf.object('right', right)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result!.x = left.x * right.x
        result!.y = left.y * right.y
        result!.z = left.z! * right.z!
        return result
    }
    /**
     * 计算两个笛卡尔坐标系的分量商。
     *
     * @param {Cartesian3} left The first Cartesian.
     * @param {Cartesian3} right The second Cartesian.
     * @param {Cartesian3} result The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter.
     */
    public static divideComponents(
        left: ICartesian,
        right: ICartesian,
        result?: ICartesian,
    ): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('left', left)
        Check.typeOf.object('right', right)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result!.x = left.x / right.x
        result!.y = left.y / right.y
        result!.z = left.z! / right.z!
        return result
    }
    /**
     * 计算两个笛卡尔坐标系的分量和。
     *
     * @param {Cartesian3} left The first Cartesian.
     * @param {Cartesian3} right The second Cartesian.
     * @param {Cartesian3} result The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter.
     */
    public static add(
        left: ICartesian, 
        right: ICartesian,
        result?: ICartesian): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('left', left)
        Check.typeOf.object('right', right)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result!.x = left.x + right.x
        result!.y = left.y + right.y
        result!.z = left.z! + right.z!
        return result
    }
    /**
     * 计算两个笛卡尔坐标系的分量差。
     *
     * @param {Cartesian3} left The first Cartesian.
     * @param {Cartesian3} right The second Cartesian.
     * @param {Cartesian3} result The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter.
     */
    public static subtract(
        left: ICartesian,
        right: ICartesian, 
        result?: ICartesian): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('left', left)
        Check.typeOf.object('right', right)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result!.x = left.x - right.x
        result!.y = left.y - right.y
        result!.z = left.z! - right.z!
        return result
    }
    /**
     * 将提供的笛卡尔分量与提供的标量逐个相乘。
     *
     * @param {Cartesian3} cartesian The Cartesian to be scaled.
     * @param {number} scalar The scalar to multiply with.
     * @param {Cartesian3} result The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter.
     */
    public static multiplyByScalar(
        cartesian: ICartesian,
        scalar: number,
        result?: ICartesian,
    ): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('cartesian', cartesian)
        Check.typeOf.number('scalar', scalar)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result!.x = cartesian.x * scalar
        result!.y = cartesian.y * scalar
        result!.z = cartesian.z! * scalar
        return result
    }
    /**
     * 将提供的笛卡尔分量除以提供的标量。
     *
     * @param {Cartesian3} cartesian The Cartesian to be divided.
     * @param {number} scalar The scalar to divide by.
     * @param {Cartesian3} result The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter.
     */
    public static divideByScalar(
        cartesian: ICartesian,
        scalar: number,
        result?: ICartesian,
    ): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('cartesian', cartesian)
        Check.typeOf.number('scalar', scalar)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result!.x = cartesian.x / scalar
        result!.y = cartesian.y / scalar
        result!.z = cartesian.z! / scalar
        return result
    }
    /**
     * 否定了提供的笛卡尔坐标系。(反向/负向 赋值)
     *
     * @param {Cartesian3} cartesian The Cartesian to be negated.
     * @param {Cartesian3} result The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter.
     */
    public static negate(
        cartesian: ICartesian,
        result?: ICartesian): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('cartesian', cartesian)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result!.x = -cartesian.x
        result!.y = -cartesian.y
        result!.z = -cartesian.z!
        return result
    }
    /**
     * 计算提供的笛卡尔坐标的绝对值。
     *
     * @param {Cartesian3} cartesian The Cartesian whose absolute value is to be computed.
     * @param {Cartesian3} result The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter.
     */
    public static abs(
        cartesian: ICartesian,
        result?: ICartesian): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('cartesian', cartesian)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result!.x = Math.abs(cartesian.x)
        result!.y = Math.abs(cartesian.y)
        result!.z = Math.abs(cartesian.z!)
        return result
    }

    public static readonly lerpScratch = new Cartesian3()
    /**
     * 使用提供的笛卡尔坐标计算t处的线性插值或外推。
     * @param {Cartesian3} start The value corresponding to t at 0.0.
     * @param {Cartesian3} end The value corresponding to t at 1.0.
     * @param {number} t The point along t at which to interpolate.
     * @param {Cartesian3} result The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter.
     */
    public static lerp(
        start: ICartesian,
        end: ICartesian,
        t: number,
        result?: ICartesian,
    ): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('start', start)
        Check.typeOf.object('end', end)
        Check.typeOf.number('t', t)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        Cartesian3.multiplyByScalar(end, t, Cartesian3.lerpScratch)
        result = Cartesian3.multiplyByScalar(start, 1.0 - t, result)
        return Cartesian3.add(Cartesian3.lerpScratch, result!, result)
    }
    public static readonly angleBetweenScratch = new Cartesian3()
    public static readonly angleBetweenScratch2 = new Cartesian3()
    /**
     * Returns the angle, in radians, between the provided Cartesians.
     *
     * @param {Cartesian3} left The first Cartesian.
     * @param {Cartesian3} right The second Cartesian.
     * @returns {number} The angle between the Cartesians.
     */
    public static angleBetween(
        left: ICartesian,
        right: ICartesian): number {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('left', left)
        Check.typeOf.object('right', right)
        //>>includeEnd('debug');
        Cartesian3.normalize(left, Cartesian3.angleBetweenScratch)
        Cartesian3.normalize(right, Cartesian3.angleBetweenScratch2)
        const cosine = Cartesian3.dot(
            Cartesian3.angleBetweenScratch, 
            Cartesian3.angleBetweenScratch2)
        const sine = Cartesian3.magnitude(
            Cartesian3.cross(
                Cartesian3.angleBetweenScratch,
                Cartesian3.angleBetweenScratch2,
                Cartesian3.angleBetweenScratch,
            ) as ICartesian,
        )
        return Math.atan2(sine, cosine)
    }

    public static readonly mostOrthogonalAxisScratch = new Cartesian3()
    /**
     * 返回与提供的笛卡尔坐标系最正交的轴。
     *
     * @param {Cartesian3} cartesian The Cartesian on which to find the most orthogonal axis.
     * @param {Cartesian3} result The object onto which to store the result.
     * @returns {Cartesian3} The most orthogonal axis.
     */
    public static mostOrthogonalAxis(
        cartesian: ICartesian, 
        result?: ICartesian): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('cartesian', cartesian)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        const f = Cartesian3.normalize(cartesian, Cartesian3.mostOrthogonalAxisScratch)
        Cartesian3.abs(f!, f)

        if (f!.x <= f!.y) {
            if (f!.x <= f!.z!) {
                result = defaultValue(
                    Cartesian3.clone(Cartesian3.UNIT_X, result),
                    new Cartesian3(1.0, 0, 0),
                )
            } else {
                result = defaultValue<Cartesian3>(
                    Cartesian3.clone(Cartesian3.UNIT_Z, result) as Cartesian3,
                    new Cartesian3(0, 0, 1.0),
                )
            }
        } else if (f!.y <= f!.z!) {
            result = defaultValue<Cartesian3>(
                Cartesian3.clone(Cartesian3.UNIT_Y, result)as Cartesian3,
                new Cartesian3(0, 1.0, 0))
        } else {
            result = defaultValue<Cartesian3>(
                Cartesian3.clone(Cartesian3.UNIT_Z, result)as Cartesian3,
                new Cartesian3(0, 0, 1.0))
        }
        return result
    }
    /**
     * 将向量a投影到向量b上
     * @param {Cartesian3} a The vector that needs projecting
     * @param {Cartesian3} b The vector to project onto
     * @param {Cartesian3} result The result cartesian
     * @returns {Cartesian3} The modified result parameter
     */
    public static projectVector(
        a: ICartesian, b: ICartesian, 
        result?: ICartesian): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.defined('a', a)
        Check.defined('b', b)
        Check.defined('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        const scalar = Cartesian3.dot(a, b) / Cartesian3.dot(b, b)
        return Cartesian3.multiplyByScalar(b, scalar, result)
    }
    /**
     * 按分量比较提供的Cartesian坐标，并返回
     * <code>true</code> if they are equal, <code>false</code> otherwise.
     *
     * @param {Cartesian3} [left] The first Cartesian.
     * @param {Cartesian3} [right] The second Cartesian.
     * @returns {boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise.
     */
    public static equals(left: ICartesian, right: ICartesian): boolean {
        return (
        left === right ||
        (defined(left) &&
            defined(right) &&
            left.x === right.x &&
            left.y === right.y &&
            left.z === right.z)
        )
    }
    /**
     * @private
     */
    public static equalsArray(
        cartesian: ICartesian, array: Array<number>, offset: number): boolean {
        return (
            cartesian.x === array[offset] &&
            cartesian.y === array[offset + 1] &&
            cartesian.z === array[offset + 2]
        )
    }
    /**
     * 按分量比较提供的Cartesian坐标，并返回
     * <code>true</code> if they pass an absolute or relative tolerance test,
     * <code>false</code> otherwise.
     *
     * @param {Cartesian3} [left] The first Cartesian.
     * @param {Cartesian3} [right] The second Cartesian.
     * @param {number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing.
     * @param {number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing.
     * @returns {boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise.
     */
    public static equalsEpsilon(
        left: ICartesian,
        right: ICartesian,
        relativeEpsilon: number,
        absoluteEpsilon: number = relativeEpsilon,
    ): boolean {
        return (
        left === right ||
        (defined(left) &&
            defined(right) &&
            HYXHMathUtil.equalsEpsilon(left.x, right.x, relativeEpsilon, absoluteEpsilon) &&
            HYXHMathUtil.equalsEpsilon(left.y, right.y, relativeEpsilon, absoluteEpsilon) &&
            HYXHMathUtil.equalsEpsilon(left.z!, right.z!, relativeEpsilon, absoluteEpsilon))
        )
    }
    /**
     * 计算两个笛卡尔坐标的交叉（外）积。
     *
     * @param {Cartesian3} left The first Cartesian.
     * @param {Cartesian3} right The second Cartesian.
     * @param {Cartesian3} result The object onto which to store the result.
     * @returns {Cartesian3} The cross product.
     */
    public static cross(
        left: ICartesian, right: ICartesian, 
        result?: ICartesian): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('left', left)
        Check.typeOf.object('right', right)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        const leftX = left.x
        const leftY = left.y
        const leftZ = left.z
        const rightX = right.x
        const rightY = right.y
        const rightZ = right.z

        const x = leftY * rightZ! - leftZ! * rightY
        const y = leftZ! * rightX - leftX * rightZ!
        const z = leftX * rightY - leftY * rightX

        result!.x = x
        result!.y = y
        result!.z = z
        return result
    }
    /**
     * 计算右和左笛卡尔坐标的中点。
     * @param {Cartesian3} left The first Cartesian.
     * @param {Cartesian3} right The second Cartesian.
     * @param {Cartesian3} result The object onto which to store the result.
     * @returns {Cartesian3} The midpoint.
     */
    public static midpoint(
        left: ICartesian, right: ICartesian,
        result?: ICartesian): ICartesian | undefined {
        //>>includeStart('debug', pragmas.debug);
        Check.typeOf.object('left', left)
        Check.typeOf.object('right', right)
        Check.typeOf.object('result', result)
        //>>includeEnd('debug');
        if (!defined(result)) {
            result = new Cartesian3()
        }
        result!.x = (left.x + right.x) * 0.5
        result!.y = (left.y + right.y) * 0.5
        result!.z = (left.z! + right.z!) * 0.5

        return result
    }
    
    private static readonly scratchN = new Cartesian3()
    private static readonly scratchK = new Cartesian3()

    // To prevent a circular dependency, this value is overridden by Ellipsoid when Ellipsoid.default is set
    public static readonly _ellipsoidRadiiSquared = new Cartesian3(
        6378137.0 * 6378137.0,
        6378137.0 * 6378137.0,
        6356752.3142451793 * 6356752.3142451793,
    )
   
    /**
     * An immutable Cartesian3 instance initialized to (0.0, 0.0, 0.0).
     *
     * @type {Cartesian3}
     * @constant
     */
    public static readonly ZERO = Object.freeze(new Cartesian3(0.0, 0.0, 0.0))

    /**
     * An immutable Cartesian3 instance initialized to (1.0, 1.0, 1.0).
     *
     * @type {Cartesian3}
     * @constant
     */
    public static readonly ONE = Object.freeze(new Cartesian3(1.0, 1.0, 1.0))

    /**
     * An immutable Cartesian3 instance initialized to (1.0, 0.0, 0.0).
     *
     * @type {Cartesian3}
     * @constant
     */
    public static readonly UNIT_X = Object.freeze(new Cartesian3(1.0, 0.0, 0.0))

    /**
     * An immutable Cartesian3 instance initialized to (0.0, 1.0, 0.0).
     *
     * @type {Cartesian3}
     * @constant
     */
    public static readonly UNIT_Y = Object.freeze(new Cartesian3(0.0, 1.0, 0.0))

    /**
     * An immutable Cartesian3 instance initialized to (0.0, 0.0, 1.0).
     *
     * @type {Cartesian3}
     * @constant
     */
    public static readonly UNIT_Z = Object.freeze(new Cartesian3(0.0, 0.0, 1.0))

    /**
     * Duplicates this Cartesian3 instance.
     *
     * @param {Cartesian3} [result] The object onto which to store the result.
     * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided.
     */
    public clone(result?: ICartesian): ICartesian | undefined {
        return Cartesian3.clone(this, result)
    }

    /**
     * Compares this Cartesian against the provided Cartesian componentwise and returns
     * <code>true</code> if they are equal, <code>false</code> otherwise.
     *
     * @param {Cartesian3} [right] The right hand side Cartesian.
     * @returns {boolean} <code>true</code> if they are equal, <code>false</code> otherwise.
     */
    public equals(right: ICartesian): boolean {
        return Cartesian3.equals(this, right)
    }

    /**
     * Compares this Cartesian against the provided Cartesian componentwise and returns
     * <code>true</code> if they pass an absolute or relative tolerance test,
     * <code>false</code> otherwise.
     *
     * @param {Cartesian3} [right] The right hand side Cartesian.
     * @param {number} [relativeEpsilon=0] The relative epsilon tolerance to use for equality testing.
     * @param {number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing.
     * @returns {boolean} <code>true</code> if they are within the provided epsilon, <code>false</code> otherwise.
     */
    public equalsEpsilon(
        right: ICartesian,
        relativeEpsilon: number,
        absoluteEpsilon: number,
    ): boolean {
        return Cartesian3.equalsEpsilon(this, right, relativeEpsilon, absoluteEpsilon)
    }
    /**
     * Creates a string representing this Cartesian in the format '(x, y, z)'.
     *
     * @returns {string} A string representing this Cartesian in the format '(x, y, z)'.
     */
    public toString(): string {
        return `(${this.x}, ${this.y}, ${this.z})`
    }

    public toQuaternion(): Quaternion {
        if (!defined(_car3_quaternion)) {
        throw new DeveloperError('quaternion is not defined.')
        }
        _car3_quaternion.x = this.x
        _car3_quaternion.y = this.y
        _car3_quaternion.z = this.z
        _car3_quaternion.w = 0.0
        return _car3_quaternion
    }
} //  Cartesian3 end class
const _car3_quaternion = new Quaternion()
